﻿using GN.Pay.Core.Entities;
using GN.Pay.Core.Repositories;
using GN.Pay.TradeChannel;
using GN.Pay.Core.Services;
using GN.Pay.Utils;
using System.Linq;
using System;
using System.Collections.Generic;
using GN.Pay.Core.Exceptions;

namespace GN.Pay.Core.Services.Impl
{
    /// <summary>
    /// 交易账本服务
    /// </summary>
    class TradeAccountBookService : APayService, ITradeAccountBookService
    {
        private readonly ITradeAccountBookRepository repostiory;
        private readonly ITradeAccountBookChannelRepository bookChannelRepository;
        private readonly ITradePayApplyRepository applyRepository;
        private readonly ITradePayApplyChannelRepository applyChannelRepository;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="repostiory"></param>
        public TradeAccountBookService(ITradeAccountBookRepository repostiory,
            ITradeAccountBookChannelRepository bookChannelRepository,
            ITradePayApplyRepository applyRepository,
            ITradePayApplyChannelRepository applyChannelRepository)
        {
            this.repostiory = repostiory;
            this.bookChannelRepository = bookChannelRepository;
            this.applyRepository = applyRepository;
            this.applyChannelRepository = applyChannelRepository;
        }

        /// <summary>
        /// 创建账本通道
        /// </summary>
        /// <param name="orderQueryResult"></param>
        /// <param name="applyTime"></param>
        /// <param name="channelId"></param>
        /// <param name="clientIP"></param>
        /// <returns></returns>
        private TradeAccountBookChannel CreateBookChannel(ITradeOrderQueryResult orderQueryResult, DateTime applyTime, string channelId, string clientIP)
        {
            var bookChannel = new TradeAccountBookChannel()
            {
                Id = orderQueryResult.OrderNo,
                ApplyTime = applyTime,
                BankName = orderQueryResult.BankName,
                BankType = orderQueryResult.BankType,
                ChannelTradeNo = orderQueryResult.TradeNo,
                BookTime = DateTime.Now,
                ChannelId = channelId,
                ClientIP = clientIP,
                OtherPartyAccount = orderQueryResult.OtherPartyAccount,
                OtherPartyAccountId = orderQueryResult.OtherPartyAccountId,
                PayFee = Math.Round(orderQueryResult.PayFee, 2),
                PayTime = orderQueryResult.PayTime,
                PayMoney = Math.Round(orderQueryResult.OrderMoney, 2),
                RefundFee = 0,
                RefundMoney = 0
            };
            bookChannel.CalcBalanceMoney();
            return bookChannel;
        }

        /// <summary>
        /// 创建账本
        /// </summary>
        /// <param name="orderQueryResult"></param>
        /// <param name="payApply"></param>
        /// <param name="appId"></param>
        /// <param name="appId"></param>
        /// <returns></returns>
        private TradeAccountBook CreateBook(ITradeOrderQueryResult orderQueryResult,
            TradePayApply payApply, string appId, string orderNo)
        {
            var book = new TradeAccountBook()
            {
                AppId = appId,
                ApplyId = payApply == null ? "" : payApply.Id,
                AppIp = payApply == null ? "" : payApply.AppIp,
                Body = payApply == null ? orderQueryResult.Body : payApply.Body,
                OrderNo = orderNo,
                OrderMoney = payApply == null ? 0m : Math.Round(payApply.OrderMoney, 2),
                OrderTime = payApply == null ? orderQueryResult.PayTime : payApply.OrderTime,
                PayCount = 1,
                MemberNo = payApply == null ? null : payApply.MemberNo,
                PayFee = Math.Round(orderQueryResult.PayFee, 2),
                PayMoney = Math.Round(orderQueryResult.OrderMoney, 2),
                PayTime = orderQueryResult.PayTime,
                FirstChannelId = orderQueryResult.ChannelId,
                RefundFee = 0,
                RefundMoney = 0,
                Remark = payApply == null ? "" : payApply.Remark,
                Subject = payApply == null ? orderQueryResult.Subject : payApply.Subject,
                BookTime = DateTime.Now
            };
            book.CalcBalanceMoney();
            return book;
        }

        /// <summary>
        /// 由申请产生的账本
        /// </summary>
        /// <param name="orderQueryResult"></param>
        /// <param name="applyChannel"></param>
        /// <returns></returns>
        private TradeAccountBookChannel AppplyAccounting(ITradeOrderQueryResult orderQueryResult, TradePayApplyChannel applyChannel)
        {
            var payApply = applyChannel.TradePayApply;
            if (payApply == null)
            {
                PayExceptionUtils.ThrowPayValidationException("无法获取 TradePayApplyChannel 父级对象，数据关系不正确。");
            }
            if (payApply.TradePayApplyChannels == null && payApply.TradePayApplyChannels.Count == 0)
            {
                PayExceptionUtils.ThrowPayValidationException("TradePayApplyChannel 无子级，数据关系不正确。");
            }
            var bookChannel = this.CreateBookChannel(orderQueryResult, payApply.ApplyTime, applyChannel.ChannelId, applyChannel.ClientIP);
            var book = repostiory.GetAll().FirstOrDefault(a => a.AppId == payApply.AppId && a.OrderNo == payApply.OrderNo);
            if (book == null)
            {
                book = this.CreateBook(orderQueryResult, payApply, payApply.AppId, payApply.OrderNo);
                book.TradeAccountBookChannels = new List<TradeAccountBookChannel>();
                book.TradeAccountBookChannels.Add(bookChannel);
                bookChannel.TradeAccountBook = book;
                this.repostiory.Insert(book);
            }
            else
            {
                book.AddChannelMoney(bookChannel);
                if (book.TradeAccountBookChannels == null)
                {
                    book.TradeAccountBookChannels = new List<TradeAccountBookChannel>();
                }
                book.TradeAccountBookChannels.Add(bookChannel);
                bookChannel.TradeAccountBook = book;
                this.repostiory.Update(book);
            }
            // payApply.TradePayApplyChannels.Remove(applyChannel);
            if (payApply.TradePayApplyChannels.Count == 1)
            {
                this.applyRepository.Delete(payApply);
            }
            else
            {
                this.applyChannelRepository.Delete(applyChannel);
                //this.applyRepository.Update(payApply);
            }
            return bookChannel;
        }

        /// <summary>
        /// 未知而产生的记账
        /// </summary>
        /// <param name="orderQueryResult"></param>
        /// <returns></returns>
        private TradeAccountBookChannel UnknownAccounting(ITradeOrderQueryResult orderQueryResult)
        {
            if (string.IsNullOrWhiteSpace(orderQueryResult.ExtraParam))
            {
                return null;
            }
            var args = orderQueryResult.ExtraParam.Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
            if (args.Length != 2 || string.IsNullOrWhiteSpace(args[0])
                || string.IsNullOrWhiteSpace(args[1]))
            {
                return null;
            }
            var bookChannel = this.CreateBookChannel(orderQueryResult, orderQueryResult.PayTime, orderQueryResult.ChannelId, "?");
            var book = this.repostiory.GetAll().FirstOrDefault(a => a.OrderNo == args[1]);
            if (book == null)
            {
                book = this.CreateBook(orderQueryResult, null, args[0], args[1]);
                book.TradeAccountBookChannels = new List<TradeAccountBookChannel>();
                book.TradeAccountBookChannels.Add(bookChannel);
                this.repostiory.Insert(book);
            }
            else
            {
                book.AddChannelMoney(bookChannel);
                if (book.TradeAccountBookChannels == null)
                {
                    book.TradeAccountBookChannels = new List<TradeAccountBookChannel>();
                }
                book.TradeAccountBookChannels.Add(bookChannel);
                this.repostiory.Update(book);
            }
            return bookChannel;
        }

        /// <summary>
        /// 记账
        /// </summary>
        /// <param name="orderQueryResult"></param>
        /// <param name="tradePayApply"></param>
        /// <returns></returns>
        public TradeAccountBookChannel Accounting(ITradeOrderQueryResult orderQueryResult,
            out TradePayApply tradePayApply)
        {
            if (orderQueryResult == null)
            {
                PayExceptionUtils.ThrowPayValidationException("orderQueryResult 为 null");
            }
            orderQueryResult.Validation();
            var bookChannel = this.bookChannelRepository.Get(orderQueryResult.OrderNo);
            if (bookChannel != null)
            {
                //已完成支付
                tradePayApply = null;
                return null;
            }
            TradePayApplyChannel applyChannel = this.applyChannelRepository.Get(orderQueryResult.OrderNo);
            if (applyChannel != null) //有申请
            {
                tradePayApply = applyChannel.TradePayApply;
                return this.AppplyAccounting(orderQueryResult, applyChannel); ;
            }
            else
            {
                //无申请,申请被删除
                tradePayApply = null;
                return this.UnknownAccounting(orderQueryResult);
            }
        }

    }
}
